using System;
    using UnityEngine.Playables;
    using System.Collections.Generic;

namespace Pvp.Timeline
{
    /// <summary>
    /// 对技能timeline 的封装，使其具备以下核心能力
    /// 1， 自定义时间轴
    /// 2， 可设定要播放的资源组
    /// 3， 播放（可支持任意资源位置播放）
    /// 4， 停止
    /// 
    /// 注意事项：
    /// 技能timeline能组装一起的前提条件是，所有track都是对roleEntity的控制，需保证这一前提条件
    /// </summary>

    [UnityEngine.RequireComponent(typeof(UnityEngine.Playables.PlayableDirector))]
    [UnityEngine.RequireComponent(typeof(FightUnitEntity))]
    public class SkillPlayDirector : UnityEngine.MonoBehaviour
    {
        private UnityEngine.Playables.PlayableDirector director;
        private LinkedList<PlayableAsset> playAssetList;
        private LinkedListNode<PlayableAsset> readyToPlay = null;
        public bool CircleMode { get; set; }

        private void Awake()
        {
            playAssetList = new LinkedList<PlayableAsset>();
            director = GetComponent<UnityEngine.Playables.PlayableDirector>();
            director.timeUpdateMode = DirectorUpdateMode.Manual;
            director.playOnAwake = false;
            director.stopped += OnDirectorStopped;
        }

        private void UpdateTime(int frameIndex, float deltaTime)
        {
            director.time += deltaTime;
            director.Evaluate();
            if (director.time >= director.playableAsset.duration)
            {
                director.Stop();
            }
        }

        private void OnDestroy()
        {
            director.stopped -= OnDirectorStopped;
        }

        void SetPlay(uint index)
        {
            if (index >= playAssetList.Count)
                throw new Exception("GetAsset error");
            var p = playAssetList.First;
            while (p != null)
            {
                if (index == 0)
                {
                    readyToPlay = p;
                    return;
                }

                --index;
                p = p.Next;
            }
        }

        public void ClearAssets()
        {
            playAssetList.Clear();
        }

        public PlayableAsset GetFirstPlayableAsset()
        {
            return playAssetList.First.Value;
        }

        public PlayableAsset GetLastPlayableAsset()
        {
            return playAssetList.Last.Value;
        }

        public void AddAssetFromHead(PlayableAsset asset)
        {
            playAssetList.AddFirst(asset);
        }

        public void AddAssetFormTrail(PlayableAsset asset)
        {
            playAssetList.AddLast(asset);
        }

        public void ForeachAsset(Action<PlayableAsset> handle)
        {
            var p = playAssetList.First;
            while (p != null)
            {
                handle?.Invoke(p.Value);
                p = p.Next;
            }
        }



        void Play(float beginTime = 0)
        {
            if (readyToPlay == null)
                readyToPlay = playAssetList.First;
            if (readyToPlay.Value == null)
                throw new Exception("Play occur unknow error");
            //所有的SkillPlayDirector全部都控制roleEntity 不需要重新绑定
            if (director.state == PlayState.Playing && director.playableAsset.Equals(readyToPlay.Value))
                return;
            director.Play(readyToPlay.Value, DirectorWrapMode.Hold);
            director.time = beginTime;

            var code = this.GetHashCode();
            if (runningMap.TryGetValue(code, out var x))
                throw new Exception("Director playedException");
            runningMap.Add(code, this);
        }

        public void PlayAt(uint assetIndex = 0, float beginTime = 0)
        {
            SetPlay(assetIndex);
            Play(beginTime);
        }

        public void Stop()
        {
            readyToPlay = null;
            director.Stop();
        }

        void OnDirectorStopped(PlayableDirector director)
        {
            var code = this.GetHashCode();
            readyToPlay = readyToPlay.Next;
            if (readyToPlay == null)
            {
                if (CircleMode)
                {
                    readyToPlay = playAssetList.First;
                    OnDirectorStopped(director);
                    return;
                }

                runningMap.Remove(code);
                return;
            }

            director.Play(readyToPlay.Value, DirectorWrapMode.Hold);
            director.time = 0;
        }

        public static void UpdateFrame(int frameIndex, float deltaTime)
        {
            if (runningMap.Count != 0)
            {
                foreach (var kvp in runningMap)
                {
                    //kvp.Value.director.time += deltaTime;
                    // kvp.Value.director.Evaluate();
                    kvp.Value.UpdateTime(frameIndex, deltaTime);
                }
            }
        }

        static Dictionary<int, SkillPlayDirector> runningMap = new Dictionary<int, SkillPlayDirector>();

    }
}